#include "lib/decoders/mod/pvmvoicesourcemod.h"
#include "lib/decoders/mod/pvmvoicemod.h"
#include "lib/pvmstrings.h"

#include "lib/memory/pvmmemory.h"

PVMVoiceSourceMOD::PVMVoiceSourceMOD()
{
	m_freq = m_virtfreq = 44100;
	m_speed = 128;

	m_mh = NULL;
	refCount = 0;

	criticalInitialized = PVMInitCritical(&critical);
}

PVMVoiceSourceMOD::~PVMVoiceSourceMOD()
{
	PVMMemoryFree(m_mh);
	m_mh = NULL;
	PVMDeinitCritical(&critical);
	criticalInitialized = false;
}

IPVMVoice* PVMVoiceSourceMOD::CreateVoice(pvm_int samplesPerSecond, pvm_int bitsPerSample, pvm_int chanelsNum)
{
	IPVMVoice *newVoice = NULL;

	//PVMEnterCritical(&critical);
	newVoice = PVMVoiceMOD::Create(this, samplesPerSecond, bitsPerSample, chanelsNum);
	//PVMLeaveCritical(&critical);

	return newVoice;
}

void PVMVoiceSourceMOD::Destroy()
{
	Release();
}

pvm_int PVMVoiceSourceMOD::GetLengthMS()
{
	return -1;//currently not supported
}

PVMVoiceSourceMOD* PVMVoiceSourceMOD::Create(void *mod)
{
	PVMVoiceSourceMOD* newSrc = new PVMVoiceSourceMOD();

	if(!newSrc)
	{
		return NULL;
	}

	newSrc->m_mh = (pvm_int8*)mod;

	/*
    for (pvm_uint32 i = 0; i < m_numchl; i += 2) { 
        m_cpan[i] = 64+v;//left
        m_cpan[i+1] = 192-v;//right
    }
	*/

    for (pvm_uint32 i = 0; i <PVMVoiceMOD::EPVM_MOD_MAXCHANNEL; i += 4)
	{ 
        newSrc->defaultChanelsPan[i] = 64;//left
        newSrc->defaultChanelsPan[i+1] = 192;//right
		newSrc->defaultChanelsPan[i+2] = 192;//right
		newSrc->defaultChanelsPan[i+3] = 64;//left
    }

	return newSrc;
}

pvm_int PVMVoiceSourceMOD::GetRefCount()
{
	pvm_int refC;
	PVMEnterCritical(&critical);
	refC = refCount;
	PVMLeaveCritical(&critical);
	return refC;
}

pvm_int PVMVoiceSourceMOD::Release()
{
	pvm_bool needRet = true;
	pvm_int newRefC = refCount;
	PVMEnterCritical(&critical);
	if(refCount>0)
	{
		refCount--;
		if(refCount<0)
			refCount = 0;
		newRefC = refCount;
		needRet = false;
	}
	else
	{
		PVMLeaveCritical(&critical);
		return 0;
	}
	PVMLeaveCritical(&critical);
	if(needRet)
		return newRefC;

	if(refCount<=0)
	{
		refCount = 0;
		delete( this);
	}
	return newRefC;
}

pvm_int PVMVoiceSourceMOD::AddRef()
{
	pvm_int newRefC;
	PVMEnterCritical(&critical);
	refCount++;
	newRefC = refCount;
	PVMLeaveCritical(&critical);
	return newRefC;
}

pvm_bool PVMVoiceSourceMOD::GetOption(const pvm_char* optionName, void* optionVal, pvm_uint32* optionSize)
{
	if(pvm_strcmp(optionName, _PVM_STR("media.mod.chanelspanning"))==0)
	{
		if(optionVal==NULL||optionSize==NULL||*optionSize<=0)
			return false;

		int chanelsToProcess = *optionSize*8;

		if(chanelsToProcess>PVMVoiceMOD::EPVM_MOD_MAXCHANNEL)
			chanelsToProcess = PVMVoiceMOD::EPVM_MOD_MAXCHANNEL;

		pvm_uint64 mask = 0;

		for (pvm_int32 i=chanelsToProcess; i>=0; i--)
		{
			if(defaultChanelsPan[i]==64)
				mask = mask | 1;

			mask = mask<<1;
		}

		switch(*optionSize)
		{
		case 1: *((pvm_uint8*)optionVal) = (pvm_uint8)mask; break;
		case 2: *((pvm_uint16*)optionVal) = (pvm_uint16)mask; break;
		case 4: *((pvm_uint32*)optionVal) = (pvm_uint32)mask; break;
		case 8: *((pvm_uint64*)optionVal) = (pvm_uint64)mask; break;
		};

		return true;
	};

	return false;
}

pvm_bool PVMVoiceSourceMOD::SetOption(const pvm_char* optionName, void* optionVal, pvm_uint32 optionSize)
{
	if(pvm_strcmp(optionName, _PVM_STR("media.mod.chanelspanning"))==0)
	{
		if(optionVal==NULL||optionSize<=0)
			return false;

		pvm_uint chanelsToProcess = optionSize*8;

		if(chanelsToProcess>PVMVoiceMOD::EPVM_MOD_MAXCHANNEL)
			chanelsToProcess = PVMVoiceMOD::EPVM_MOD_MAXCHANNEL;

		pvm_uint64 mask = 0;
		switch(optionSize)
		{
		case 1: mask = *((pvm_uint8*)optionVal); break;
		case 2: mask = *((pvm_uint16*)optionVal); break;
		case 4: mask = *((pvm_uint32*)optionVal); break;
		case 8: mask = *((pvm_uint64*)optionVal); break;
		};

		for (pvm_uint32 i=0; i<chanelsToProcess; i++)
		{
			if(mask&1)
				defaultChanelsPan[i] = 64;//left
			else
				defaultChanelsPan[i] = 192;//right

			mask = mask>>1;
		}

		return true;
	};

	return false;
}
